package com.tsmti.core.common.dao.impl;

import com.tsmti.core.common.dao.BaseDao;
import com.tsmti.core.common.entity.MarkDeleteable;
import com.tsmti.core.common.query.data.Queryable;
import com.tsmti.core.common.query.data.Sort;
import com.tsmti.core.util.BeanTransformerAdapter;
import org.hibernate.*;
import org.hibernate.criterion.*;
import org.hibernate.transform.Transformers;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.orm.hibernate4.support.HibernateDaoSupport;
import org.springframework.stereotype.Repository;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.*;

/**
 * Title：PimsSystem
 * Class：com.tsmti.core.base
 * Comapny:
 *
 * @author：zcw
 * @version：2.5 CreateTime：2018/1/4 0:04
 * Modify log:
 * Description：
 */
@Repository("baseDao")
public class BaseDaoImpl extends HibernateDaoSupport implements BaseDao {
    /**
     * 初始化Log4j的一个实例
     */
    protected Logger logger = LoggerFactory.getLogger(getClass());

    private static final String ORDER_ASC = "asc";

    /**
     * 注入一个sessionFactory属性,并注入到父类(HibernateDaoSupport)
     **/
    @Resource
    public void setSessionFacotry(SessionFactory sessionFacotry) {
        super.setSessionFactory(sessionFacotry);
    }

    public Session getSession() {
        // 事务必须是开启的(Required)，否则获取不到
        return this.getHibernateTemplate().getSessionFactory().getCurrentSession();
    }

    @Override
    public <T> T save(T entity) {
        Assert.notNull(entity, "Entity is required");
        try {
            return (T) getSession().merge(entity);
        } catch (RuntimeException e) {
            logger.error("添加异常", e);
            throw e;
        }
    }

    @Override
    public <T> void saveAll(List<T> entitys) {
        Assert.notNull(entitys, "List is required");
        for (int i = 0; i < entitys.size(); i++) {
            getSession().saveOrUpdate(entitys.get(i));
            if (i % 20 == 0) {
                getSession().flush();
                getSession().clear();
            }
        }
        getSession().flush();
        getSession().clear();
    }

    @Override
    public <T> List<T> saveList(List<T> entitys) {
        Assert.notNull(entitys, "List is required");
        List<T> list = new ArrayList<>();
        for (int i = 0; i < entitys.size(); i++) {
            list.add((T) getSession().merge(entitys.get(i)));
            if (i % 20 == 0) {
                getSession().flush();
                getSession().clear();
            }
        }
        getSession().flush();
        getSession().clear();
        return list;
    }

    @Override
    public <T> void saveOrUpdate(T entity) {
        Assert.notNull(entity, "entity is required");
        try {
            getSession().saveOrUpdate(entity);
        } catch (RuntimeException e) {
            logger.error("添加或更新失败", e);
            throw e;
        }
    }

    @Override
    public <T> void update(T entity) {
        Assert.notNull(entity, "entity is required");
        try {
            this.getHibernateTemplate().update(entity);
        } catch (RuntimeException e) {
            logger.error("更新失败", e);
            throw e;
        }
    }

//    @Override
//    public <T> T merge(T entity) {
//        return (T)getSession().merge(entity);
//    }

    @Override
    public <T> T load(Serializable id, Class<T> entityClass) {
        Assert.notNull(id, "id is required");
        try {
            T t = (T) this.getHibernateTemplate().load(entityClass, id);
            return t;
        } catch (RuntimeException e) {
            logger.error("获取失败", e);
            throw e;
        }
    }

    @Override
    public <T> T get(Serializable id, Class<T> entityClass) {
        Assert.notNull(id, "id is required");
        try {
            T t = (T) this.getHibernateTemplate().get(entityClass, id);
            return t;
        } catch (RuntimeException e) {
            logger.error("获取失败", e);
            throw e;
        }
    }

    @Override
    public <T> T get(String propertyName, Object value, Class<T> entityClass) {
        Assert.hasText(propertyName, "propertyName is required");
        Assert.notNull(value, "value is required");
        try {
            T t = (T) createCriteria(entityClass, Restrictions.eq(propertyName, value)).uniqueResult();
            return t;
        } catch (RuntimeException e) {
            logger.error("获取失败", e);
            throw e;
        }
    }

    @Override
    public <T> Boolean exists(Serializable id, Class<T> entityClass) {
        Assert.notNull(id, "id is required");
        try {
            this.getHibernateTemplate().get(entityClass, id);
            return false;
        } catch (RuntimeException e) {
            logger.error("获取失败", e);
            throw e;
        }
    }

    @Override
    public <T> Long count(Class<T> entityClass) {
        try {
            return (long) createCriteria(entityClass).list().size();
        } catch (RuntimeException e) {
            logger.error("获取失败", e);
            throw e;
        }
    }

    @Override
    public Long count(DetachedCriteria detachedCriteria) {
        Projection projection = Projections.rowCount();
        Object obj = detachedCriteria.setProjection(Projections.rowCount()).getExecutableCriteria(this.getSession()).uniqueResult();
        return (Long) obj;
    }

    @Override
    public <T> Long count(Class<T> entityClass, Criterion... criterions) {
        Criteria criteria = this.getSession().createCriteria(entityClass);
        if (criterions != null) {
            for (Criterion criterion : criterions) {
                if (criterion != null) {
                    criteria.add(criterion);
                }
            }
        }
        Long total = (Long) criteria.setProjection(Projections.rowCount()).uniqueResult();
        if (total == null) {
            total = (long) 0;
        }
        return total;
    }

    @Override
    public <T> List<T> list(Class<T> entityClass) {
        Criteria criteria = createCriteria(entityClass);
        return criteria.list();
    }


    @Override
    public <T> List<T> list(Class<T> entityClass,String propertyName, Object value) {
        Assert.hasText(propertyName, "propertyName is required");
        Assert.notNull(value, "value is required");
        try {
            return createCriteria(entityClass, Restrictions.eq(propertyName, value)).list();
        } catch (RuntimeException e) {
            logger.error("获取失败", e);
            throw e;
        }
    }

    @Override
    public <T> List<T> listByOrder(Class<T> entityClass, String orderType, String orderName) {
        Criteria criteria = createCriteria(entityClass);
        if (ORDER_ASC.equals(orderType)) {
            criteria.addOrder(Order.asc(orderName));
        } else {
            criteria.addOrder(Order.desc(orderName));
        }
        return criteria.list();
    }

    @Override
    public <T> List<T> list(DetachedCriteria detachedCriteria) {
        Criteria criteria = detachedCriteria.getExecutableCriteria(getSession());
        criteria.setProjection(null);
        List<T> content = criteria.list();
        return content;
    }

    @Override
    public <T> List<T> list(int page, int rows, DetachedCriteria detachedCriteria) {
        Criteria criteria = detachedCriteria.getExecutableCriteria(getSession());
        criteria.setProjection(null);
        criteria.setFirstResult((page - 1) * rows);
        criteria.setMaxResults(rows);
        List<T> content = criteria.list();
        return content;
    }

    @Override
    public <T> List<T> listByCriterion(Class<T> entityClass, Criterion... criterions) {
        try {
            Criteria criteria = this.getSession().createCriteria(entityClass);
            if (criterions != null) {
                for (Criterion criterion : criterions) {
                    if (criterion != null) {
                        criteria.add(criterion);
                    }
                }
            }
            return criteria.list();
        } catch (RuntimeException re) {
            throw re;
        } finally {

        }
    }

    @Override
    public <T> List<T> listByCriterion(Class<T> entityClass, String orderType, String orderName, Criterion... criterions) {
        try {
            Criteria criteria = this.getSession().createCriteria(entityClass);
            if (criterions != null) {
                for (Criterion criterion : criterions) {
                    if (criterion != null) {
                        criteria.add(criterion);
                    }
                }
            }
            if (ORDER_ASC.equals(orderType)) {
                criteria.addOrder(Order.asc(orderName));
            } else {
                criteria.addOrder(Order.desc(orderName));
            }
            return criteria.list();
        } catch (RuntimeException re) {
            throw re;
        } finally {

        }
    }

    @Override
    public <T> List<T> listByCriterion(Class<T> entityClass, int page, int rows, Criterion... criterions) {
        try {
            Criteria criteria = this.getSession().createCriteria(entityClass);
            if (criterions != null) {
                for (Criterion criterion : criterions) {
                    if (criterion != null) {
                        criteria.add(criterion);
                    }
                }
            }
            criteria.setFirstResult((page - 1) * rows);
            criteria.setMaxResults(rows);
            return criteria.list();
        } catch (RuntimeException re) {
            throw re;
        } finally {
        }
    }

    @Override
    public <T> List<T> listByCriterion(Class<T> entityClass, int page, int rows, String orderType, String orderName, Criterion... criterions) {
        Criteria criteria = this.getSession().createCriteria(entityClass);
        if (criterions != null) {
            for (Criterion criterion : criterions) {
                if (criterion != null) {
                    criteria.add(criterion);
                }
            }
        }
        if (ORDER_ASC.equals(orderType)) {
            criteria.addOrder(Order.asc(orderName));
        } else {
            criteria.addOrder(Order.desc(orderName));
        }
        criteria.setFirstResult((page - 1) * rows);
        criteria.setMaxResults(rows);
        return criteria.list();
    }

    @Override
    public <T> List<T> listPageBySql(Class<T> entityClass,int page, int rows,String sql, Object... params) {
        SQLQuery query = this.getSession().createSQLQuery(sql);
        query.setResultTransformer(new BeanTransformerAdapter(entityClass));
        setParameters(query, params);
        query.setFirstResult((page - 1) * rows);
        query.setMaxResults(rows);
        return query.list();
    }

    @Override
    public <T> List<T> listBySql(Class<T> entityClass,String sql,  Object... params) {
        SQLQuery query = this.getSession().createSQLQuery(sql);
        query.setResultTransformer(new BeanTransformerAdapter(entityClass));
        setParameters(query, params);
        return query.list();
    }

    @Override
    public <T> List<T> listByAliasSql(Class<T> entityClass, String sql, Map<String, Object> params) {
        SQLQuery query = this.getSession().createSQLQuery(sql);
        query.setResultTransformer(new BeanTransformerAdapter(entityClass));
        setAliasParameter(query, params);
        return query.list();
    }

    @Override
    public <T> List<T> listPageByAliasSql(Class<T> entityClass, int page, int rows, String sql, Map<String, Object> params) {
        SQLQuery query = this.getSession().createSQLQuery(sql);
        query.setResultTransformer(new BeanTransformerAdapter(entityClass));
        setAliasParameter(query, params);
        query.setFirstResult((page - 1) * rows);
        query.setMaxResults(rows);
        return query.list();
    }


    @Override
    public <T> void remove(T entity) {
        Assert.notNull(entity, "entity is required");
        try {
            if (entity instanceof MarkDeleteable) {
                MarkDeleteable markDeleteable = (MarkDeleteable) entity;
                if (markDeleteable.markStatus()) {
                    markDeleteable.markDelete(Boolean.TRUE);
                    this.getHibernateTemplate().saveOrUpdate(entity);
                } else {
                    this.getHibernateTemplate().delete(entity);
                }
            } else {
                this.getHibernateTemplate().delete(entity);
            }
        } catch (RuntimeException e) {
            logger.error("删除失败", e);
            throw e;
        }
    }

    @Override
    public <T> void removeById(Serializable id, Class<T> entityClass) {
        Assert.notNull(id, "id is required");
        try {
            remove(get(id, entityClass));
        } catch (RuntimeException e) {
            logger.error("删除失败", e);
            throw e;
        }
    }

    @Override
    public <T> void removeAll(List<T> entitys) {
        for (int i = 0; i < entitys.size(); i++) {
            remove(entitys.get(i));
            if (i % 20 == 0) {
                getSession().flush();
                getSession().clear();
            }
        }
        getSession().flush();
        getSession().clear();
    }

    @Override
    public <T> void removeAllByProperty(String propertyName, Object value, Class<T> entityClass) {
        Assert.hasText(propertyName, "propertyName is required");
        Assert.notNull(value, "value is required");
        try {
            List<T> list = createCriteria(entityClass, Restrictions.eq(propertyName, value)).list();
            removeAll(list);
        } catch (RuntimeException e) {
            logger.error("删除失败", e);
            throw e;
        }
    }

    @Override
    public Integer updateByHql(String hql, Object... params) {
        Query query = getSession().createQuery(hql);
        setParameters(query, params);
        return query.executeUpdate();
    }

    @Override
    public Integer updateByAliasHql(String hql, Map<String, Object> alias) {
        Query query = getSession().createQuery(hql);
        setAliasParameter(query, alias);
        return query.executeUpdate();
    }

    @Override
    public Integer updateBySql(String sql, Object... params) {
        Query query = getSession().createSQLQuery(sql);
        setParameters(query, params);
        return query.executeUpdate();
    }

    @Override
    public Integer updateByAliasSql(String sql, Map<String, Object> alias) {
        Query query = getSession().createSQLQuery(sql);
        setAliasParameter(query, alias);
        return query.executeUpdate();
    }

    @Override
    public Long countByHql(String hql, Object... params) {
        Query query = getSession().createQuery(hql);
        setParameters(query, params);
        return (Long) query.uniqueResult();
    }

    @Override
    public Long countByAliasHql(String hql, Map<String, Object> alias) {
        Query query = getSession().createQuery(hql);
        setAliasParameter(query, alias);
        return (Long) query.uniqueResult();
    }

    @Override
    public Long countBySql(String sql,  Object... params) {
        Query query = getSession().createSQLQuery(sql);
        setParameters(query,params);
        Object total = query.uniqueResult();
        if(total == null){
            return 0L;
        }
        return ((BigDecimal)query.uniqueResult()).longValue();
    }
    @Override
    public Long countByAliasSql(String sql, Map<String, Object> alias) {
        Query query = getSession().createSQLQuery(sql);
        setAliasParameter(query, alias);
        Object total = query.uniqueResult();
        if(total == null){
            return 0L;
        }
        return ((BigDecimal)query.uniqueResult()).longValue();
    }

    @Override
    public <T> List<T> findByHql(Class<T> entityClass, String hql, Object... params) {
        Query query = getSession().createQuery(hql);
        setParameters(query, params);
        return query.list();
    }

    @Override
    public <T> List<T> findByAliasHql(String hql, Map<String, Object> alias) {
        Query query = getSession().createQuery(hql);
        setAliasParameter(query, alias);
        return query.list();
    }

    @Override
    public <T> List<T> findByHql(String hql, int page, int rows, Object... params) {
        Query query = getSession().createQuery(hql);
        if (params != null) {
            for (int i = 0; i < params.length; i++) {
                query.setParameter(i, params[i]);
            }
        }
        query.setFirstResult((page - 1) * rows);
        query.setMaxResults(rows);
        return query.list();
    }

    @Override
    public <T> List<T> findByAliasHql(String hql, int page, int rows, Map<String, Object> alias) {
        Query query = getSession().createQuery(hql);
        setAliasParameter(query, alias);
        query.setFirstResult((page - 1) * rows);
        query.setMaxResults(rows);
        return query.list();
    }

    @Override
    public List<Map<String, Object>> findMapByHql(String hql, Object... params) {
        Query query = getSession().createQuery(hql);
        query.setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
        if (params != null) {
            for (int i = 0; i < params.length; i++) {
                query.setParameter(i, params[i]);
            }
        }
        return query.list();
    }

    @Override
    public List<Map<String, Object>> findMapByAliasHql(String hql, Map<String, Object> alias) {
        Query query = getSession().createQuery(hql);
        query.setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
        setAliasParameter(query, alias);
        return query.list();
    }

    private <T> Criteria createCriteria(Class<T> entityClass, Criterion... criterions) {
        Criteria criteria = getSession().createCriteria(entityClass);
        for (Criterion c : criterions) {
            criteria.add(c);
        }
        return criteria;
    }

    /**
     * 按顺序设置Query参数
     *
     * @param query
     * @param params
     */
    public void setParameters(Query query, Object[] params) {
        if (params != null) {
            for (int i = 0; i < params.length; i++) {
                query.setParameter(i, params[i]);
            }
        }
    }

    @SuppressWarnings("rawtypes")
    public void setAliasParameter(Query query, Map<String, Object> alias) {
        if (alias != null) {
            Set<String> keys = alias.keySet();
            for (String key : keys) {
                Object val = alias.get(key);
                if(val != null) {
                    if (val instanceof Collection) {
                        // 查询条件是列表
                        query.setParameterList(key, (Collection) val);
                    } else if(val instanceof String){
                        if(!StringUtils.isEmpty(val)){
                            query.setParameter(key, val);
                        }
                    }else {
                        query.setParameter(key, val);
                    }
                }
            }
        }
    }

    /**
     * @param s sql
     * @return
     */
    public Integer countSqlTotal(String s) {
        Query query = getSession().createSQLQuery(s);
        Object total = query.uniqueResult();
        if (total == null) {
            return 0;
        }
        return ((BigDecimal) query.uniqueResult()).intValue();
    }

    /**
     * 根据queryable返回的排序拼接排序
     *
     * @param sql,queryable
     * @return
     */
    public String sortByQueryable(String sql, Queryable queryable) {
        if (queryable.getPageable().getSort() != null) {
            sql += " order by ";
            StringBuilder sort =  new StringBuilder("");
            Iterator<Sort.Order> sortList = queryable.getPageable().getSort().iterator();
            while (sortList.hasNext()) {
                Sort.Order order = sortList.next();
                String sidx = order.getProperty();
                String derection = order.getDirection().toString();
                if(StringUtils.isEmpty(sort.toString())){
                    //排序
                    sort.append(sidx + " " + derection);
                }else{
                    sort.append(","+sidx + " " + derection);
                }
            }
            sql = sql + sort.toString();
        }
        return sql;
    }

    /**
     *
     * @param queryable
     * @return
     */
    public int getStartPageNum(Queryable queryable){
        int start =0;
        if (queryable.getPageable().getPageNumber() != 1) {
            start = (queryable.getPageable().getPageNumber()-1) * queryable.getPageable().getPageSize();
        }
        return start;
    }

    /**
     *
     * @param queryable
     * @return
     */
    public int getEndPageNum(Queryable queryable){
        int end =queryable.getPageable().getPageSize();
        if (queryable.getPageable().getPageNumber() != 1) {
            end = queryable.getPageable().getPageNumber()* queryable.getPageable().getPageSize();
        }
        return  end;
    }

    /**
     *
     * @param entityClass
     * @param hql
     * @param params
     * @param <T>
     * @return
     */
    public <T> List<T> findByHqls(Class<T> entityClass, String hql, Object... params) {
        Query query = getSession().createQuery(hql);
        setParameters(query, params);
        return query.setResultTransformer(Transformers.aliasToBean(entityClass)).list();
    }
}
